package apiserver

import (
	"encoding/json"
	"fmt"
	"net"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"

	"github.com/hprose/hprose-go"

	"yingsheng.com/go-api-server/config"
	"yingsheng.com/go-api-server/context"
	"yingsheng.com/go-api-server/errors"
	"yingsheng.com/go-api-server/log"
	"yingsheng.com/go-api-server/module"
	"yingsheng.com/go-api-server/mq"
	"yingsheng.com/go-api-server/orm"
	"yingsheng.com/go-api-server/utils"
)

type TaskFunc func() time.Duration

var ApiApp *App

func init() {
	// 创建配置对象
	config.NewConfiger("conf/api.conf")

	// 创建日志对象，必须在配置对象初始化之后定义
	log.NewLogger("system")

	NewApp()

	var err error

	//初始化数据库连接池
	_, err = orm.NewEngine()
	if err != nil {
		log.Logger.Critical(err)
		os.Exit(1)
	}

	ApiApp.ChainProducer, err = mq.NewProducer("nsq", "chainlog")
	if err != nil {
		log.Logger.Critical("NSQ服务Producer创建失败：", err)
		os.Exit(1)
	}

	// 设置消费者异常捕获日志记录
	mq.SetRecoverConsumerPanic(ApiApp.RecoverConsumerPanic)

	// 关闭初始化阻塞
	close(ApiApp.initChan)
}

type App struct {
	Handlers     *ControllerRegister
	httpListener net.Listener

	Rpc         *hprose.WebSocketService
	rpcListener net.Listener

	waitGroup  utils.WaitGroup
	taskFunc   []TaskFunc
	filterFunc []FilterFunc

	Module        *module.Client
	ChainProducer mq.ProducerInterface

	initChan chan bool
}

// NewApp returns a new ApiApp application.
func NewApp() *App {
	ApiApp = &App{
		// HTTP服务对象（控制器注册器）
		Handlers: NewControllerRegister(),
		// RPC服务对象
		Rpc: hprose.NewWebSocketService(),
		// App初始化通知
		initChan: make(chan bool),
	}

	return ApiApp
}

func (app *App) Wait() {
	<-app.initChan
}

// Run ApiApp application.
func (app *App) Run() {
	var err error

	apiConf := &config.Configer.ApiConf

	//注册服务
	app.Module, err = module.NewClient(apiConf.ConsulServer, apiConf.ConsulPort)
	if err != nil {
		log.Logger.Critical("Consul服务器连接失败：", err)
		os.Exit(1)
		return
	} else {
		err = app.Module.Register(
			apiConf.ModuleName,
			apiConf.Server,
			apiConf.HttpPort,
			apiConf.RpcPort,
			apiConf.ModuleInterval,
		)
		if err != nil {
			log.Logger.Critical(apiConf.ModuleName+"服务注册失败：", err)
		} else {
			log.Logger.Info(apiConf.ModuleName + "服务注册成功")
		}
	}

	//app.Server.Addr = app.AppConf.Server + ":" + app.AppConf.HttpPort
	//app.Server.Handler = app.Handlers

	//读的最大Timeout时间
	//app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
	//写的最大Timeout时间
	//app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second

	//启动服务
	runInfo := apiConf.AppName + " " + apiConf.Version
	if config.Configer.IsDev() {
		runInfo += " 本地开发模式"
	}

	//启动HTTP API服务
	httpAddress := apiConf.Server + ":" + apiConf.HttpPort
	httpListener, err := net.Listen("tcp", httpAddress)
	if err != nil {
		log.Logger.Critical(runInfo+" API服务启动失败", err)
		os.Exit(1)
	}
	app.httpListener = httpListener
	app.waitGroup.Wrap(func() {
		log.Logger.Info(runInfo + " API服务启动在：" + httpAddress)

		server := &http.Server{Handler: app.Handlers}
		err = server.Serve(httpListener)
		if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
			log.Logger.Critical(runInfo+" API服务运行出错", err)
		}

		log.Logger.Info(runInfo + " API服务停止监听")
	})

	//启动RPC服务
	rpcAddress := apiConf.Server + ":" + apiConf.RpcPort
	rpcListener, err := net.Listen("tcp", rpcAddress)
	if err != nil {
		log.Logger.Critical(runInfo+" RPC服务启动失败", err)
		os.Exit(1)
	}
	app.rpcListener = rpcListener
	//if len(app.Rpc.MethodNames) > 0 {
	app.waitGroup.Wrap(func() {
		log.Logger.Info(runInfo + " RPC服务启动在：" + rpcAddress)

		server := &http.Server{Handler: app.Rpc}
		err = server.Serve(rpcListener)
		if err != nil && !strings.Contains(err.Error(), "use of closed network connection") {
			log.Logger.Critical(runInfo+" RPC服务运行出错", err)
		}

		log.Logger.Info(runInfo + " RPC服务停止监听")
	})
	//}

	//启动任务
	for i, n := 0, len(app.taskFunc); i < n; i++ {
		app.waitGroup.Task(app.taskFunc[i])
	}
}

// Stop ApiApp application
func (app *App) Exit() {
	err := app.Module.DeRegister()
	if err != nil {
		log.Logger.Critical(config.Configer.ApiConf.ModuleName+"服务反注册失败：", err)
	} else {
		log.Logger.Info(config.Configer.ApiConf.ModuleName + "服务反注册成功")
	}

	app.waitGroup.StopTask()

	if app.httpListener != nil {
		app.httpListener.Close()
	}

	if app.rpcListener != nil {
		app.rpcListener.Close()
	}

	mq.Exit()

	app.waitGroup.Wait()
}

func (app *App) RecoverConsumerPanic(err errors.RecoverErrorInterface) {
	app.RecoverPanic("consumer", err)
}

func (app *App) RecoverPanic(funcType string, err ...errors.RecoverErrorInterface) {
	var errorInfo string

	switch funcType {
	case "task":
		if e := recover(); e != nil {
			errorInfo = fmt.Sprintf("系统故障: %v", e)
		}
	case "consumer":
		if len(err) == 1 {
			errorInfo = fmt.Sprintf("系统故障：%v", err[0].Error())
		}
	}

	//发生错误
	if len(errorInfo) > 0 {
		chainInfo := context.ChainInfo{}

		errorInfo += "\n故障堆栈："
		for i := 1; ; i += 1 {
			_, file, line, ok := runtime.Caller(i)
			if !ok {
				break
			} else {
				errorInfo += "\n"
			}
			errorInfo += fmt.Sprintf("%v %v", file, line)
		}

		chainInfo.Action = funcType
		chainInfo.Host = config.Configer.ApiConf.Server + ":" + config.Configer.ApiConf.HttpPort
		chainInfo.ReferAction = config.Configer.ApiConf.ModuleName
		chainInfo.ErrorInfo = errorInfo
		chainInfo.CreateTime = time.Now().Unix()

		j, e := json.Marshal(&chainInfo)
		if e == nil {
			e = app.ChainProducer.Publish(j)
		} else {
			e = errors.New("app recover panic json marshal error: " + e.Error())
		}

		if e != nil {
			log.Logger.Error(funcType+"崩溃日志发送消息失败：", e)
		}
	}
}
